﻿using SuperX.Common;
using SuperX.Common.BasicService;
using SuperX.Common.Code;
using SuperX.Common.Helper;
using SuperX.Common.Log;
using SuperX.Common.Object;
using SuperX.DataService.Abstract;
using Newtonsoft.Json;
using NPOI.SS.Formula.Functions;
using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;

namespace SuperX.DataService.Abstract
{
    public class DynamicTag : BasicObject, IIOTag
    {
        protected dynamic _value;
        private object _rawValue;
        private object writeLock = new object();

        public override string ServiceType
        {
            get => typeof(IOTag<T>).FullName + "," + typeof(IOTag<T>).Assembly.ManifestModule.Name;
            set { }
        }

        protected bool _active = true;

        /// <summary>
        /// 是否激活
        /// </summary>
        public bool Active
        {
            get
            {
                return _active;
            }
            set
            {
                _group.SetActiveState(value, Identity);
                _active = value;
            }
        }

        [JsonIgnore]
        object ITag.Value
        {
            get => Value;
            set => WriteTagValue(value);
        }

        /// <summary>
        /// 变量值
        /// </summary>
        [JsonIgnore]
        public virtual dynamic Value
        {
            get => _value;
            set => WriteTagValue(value);
        }

        public void WriteTagValue(dynamic value)
        {
            if (ReadWriteMode == ReadWriteMode.ReadOnly) return;
            WriteTagValue(value, QUALITIES.OK, DateTime.Now);
        }

        /// <summary>
        /// 写入值
        /// </summary>
        public dynamic WriteValue
        {
            get => _writeValue;
            set => _writeValue = value;
        }

        protected dynamic _writeValue;

        protected DateTime _timeStamp = DateTime.MinValue;

        /// <summary>
        /// 时间戳
        /// </summary>
        public DateTime TimeStamp
        {
            get
            {
                return _timeStamp;
            }
            set
            {
                _timeStamp = value;
            }
        }

        protected DeviceAddress _plcAddress;

        /// <summary>
        /// 地址
        /// </summary>
        public DeviceAddress Address
        {
            get
            {
                return _plcAddress;
            }
            set
            {
                _plcAddress = value;
            }
        }

        protected IGroup _group;

        [JsonIgnore]
        public IGroup ParentGroup
        {
            get
            {
                return _group;
            }
        }

        public HDASetting HDASetting { get; set; }

        /// <summary>
        /// 是否存档
        /// </summary>
        public bool Archive { get; set; }

        /// <summary>
        /// 间隔触发值变化
        /// </summary>
        public double Interval { get; set; }

        /// <summary>
        /// 主键
        /// </summary>
        public string Key
        {
            get => this.FullName;
        }

        /// <summary>
        /// 读写模式
        /// </summary>
        public ReadWriteMode ReadWriteMode { get; set; }

        /// <summary>
        /// 脚本
        /// </summary>
        public string Script { get; set; }

        /// <summary>
        /// 写入时的转换
        /// </summary>
        public string WriteScript { get; set; }

        public virtual long Maximum { get; set; }

        public virtual long Minimum { get; set; }

        public string Regester { get; set; }

        public string No { get; set; }

        public TagType TagType { get; set; }
        object ITag.DefaultValue { get; set; }

        bool ITag.IsReadOnly { get; set; }
        Type ITag.ValueType { get; set; }

        [JsonIgnore]
        public dynamic RawValue
        {
            get
            {
                if (_rawValue == null && Common.Helper.PythonScriptHelper.IsScriptEditMode) return new object();
                return _rawValue;
            }
            set
            {
                _rawValue = value;
            }
        }

        public object InitValue { get => _initValue; set => _initValue = value; }
        private object _initValue = default(T);

        protected QUALITIES _quality;

        /// <summary>
        /// 质量戳
        /// </summary>
        public QUALITIES Quality
        {
            get
            {
                return _quality;
            }
            set { _quality = value; }
        }

        protected QUALITIES _writeQuality;

        /// <summary>
        /// 写入质量戳
        /// </summary>
        public QUALITIES WriteQuality
        {
            get => _writeQuality;
            set { _writeQuality = value; }
        }

        /// <summary>
        /// 转换比例值
        /// </summary>
        public float ScaleValue { get; set; }

        public TagConvertType ConverterType { get; set; }
        public object DefaultValue { get; set; }
        public bool IsReadOnly { get; set; }

        public string Unit { get; set; }

        [JsonIgnore]
        public virtual Type ValueType { get; set; } = typeof(object);

        public event EventHandler<TagChangedEventArgs> TimeTriggered;

        public event EventHandler<TagChangingEventArgs> ValueChanging;

        public event EventHandler<TagChangedEventArgs> ValueChanged;

        public DynamicTag(DeviceAddress address, IGroup group)
        {
            _group = group;
            _plcAddress = address;
        }

        /// <summary>
        /// 主动触发时间事件
        /// </summary>
        /// <param name="timeStamp"></param>
        /// <param name="quality"></param>
        public void Update(object newvalue, DateTime timeStamp, QUALITIES quality)
        {
            if (_timeStamp > timeStamp) return;//如果时间戳更旧或值未改变
            _timeStamp = timeStamp;
            _quality = quality;
            if (quality == QUALITIES.OK)
            {
                if (TimeTriggered != null)
                {
                    TimeTriggered(this, new TagChangedEventArgs(this));
                }
            }
        }

        public void UpdateTagValue(object rawValue)
        {
            UpdateIoTagValue(rawValue, QUALITIES.OK, DateTime.Now);
        }

        public void UpdateIoTagValue(object rawValue, QUALITIES quality, DateTime timeStamp)
        {
            var oldValue = RawValue;
            if (rawValue.GetType().IsValueType && Value != null)
            {
                Cast(rawValue, Value.GetType(), out object ev);
                RawValue = ev;
            }
            else
            {
                RawValue = rawValue; //Io读取值
            }
            OnRawValueChanged(new TagChangedEventArgs(this, ValueChangedType.UpdateValueChanged, oldValue, RawValue, FullName));
            var flag = SetValue(rawValue, out var tagValue);
            UpdateTagValue(tagValue, flag ? quality : QUALITIES.NG, timeStamp, FullName);
        }

        public virtual void ExecuteExpression()
        {
            if (TagType != TagType.Exclusive) return;
            try
            {
                var project = BasicServiceManager.GetProjService().Project;
                var result = project.CodeManager.Execute(Script, project);
                UpdateTagValue(result);
            }
            catch (Exception ex)
            {
                Logger.Warn(string.Format("Tag {0} execute script converter fail: {1}{2}", FullName, Environment.NewLine, ex.Message));
            }
        }

        public bool SetValue(object rawValue, out object tagValue)
        {
            tagValue = rawValue;
            switch (ConverterType)
            {
                case TagConvertType.None:
                    break;

                case TagConvertType.Expression:
                    if (string.IsNullOrEmpty(Script)) tagValue = rawValue;
                    else
                    {
                        if (!ExecuteExpression(Script, out tagValue))
                        {
                            return false;
                        }
                    }
                    break;

                default:
                    tagValue = rawValue;
                    break;
            }
            return true;
        }

        public virtual bool ExecuteExpression(string expression, out dynamic returnValue)
        {
            try
            {
                var project = BasicServiceManager.GetProjService().Project;
                returnValue = project.CodeManager.Execute(expression, project);
                return true;
            }
            catch (Exception ex)
            {
                Logger.Warn(string.Format("IOTag {0} execute expression converter fail: {1}{2}", FullName, Environment.NewLine, ex.Message));
                returnValue = new object();
                return false;
            }
        }

        public bool Cast(object value, Type type, out object castValue)
        {
            try
            {
                castValue = Convert.ChangeType(value, type);
                return true;
            }
            catch (Exception)
            {
                castValue = value;
                return false;
            }
        }

        public event EventHandler<TagChangedEventArgs> RawValueChanged;

        public void OnRawValueChanged(TagChangedEventArgs args)
        {
            try
            {
                RawValueChanged?.Invoke(this, args);
            }
            catch
            {
            }
        }

        public void UpdateQAndS(DateTime timeStamp, QUALITIES quality)
        {
            TimeStamp = timeStamp;
            if (Quality == quality)
                return;
            Quality = quality;
        }

        public void UpdateTagValue(object value, QUALITIES quality, DateTime timeStamp, string updateSourceName)
        {
            lock (writeLock)
            {
                TimeStamp = timeStamp;
            }
            if (!IsValidValue(value, out dynamic actualValue))
            {
                UpdateQAndS(timeStamp, quality);
                return;
            }
            if (OnValueChanging(new TagChangingEventArgs(this)))
            {
                UpdateQAndS(timeStamp, quality);
                return;
            }
            lock (writeLock)
            {
                var oldValue = _value;
                _value = actualValue;
                _quality = quality;
                OnValueChanged(new TagChangedEventArgs(this, ValueChangedType.UpdateValueChanged, oldValue, _value, updateSourceName));
            }
        }

        public void WriteTagValue(object value, QUALITIES quality, DateTime timeStamp)
        {
            if (ReadWriteMode == ReadWriteMode.ReadOnly || !IsValidValue(value, out dynamic actualValue))
            {
                return;
            }
            if (OnValueChanging(new TagChangingEventArgs(this)))
            {
                return;
            }
            var oldValue = _value;
            _value = value;
            _quality = quality;
            ValueType = _value.GetType();
            OnValueChanged(new TagChangedEventArgs(this, ValueChangedType.ValueChanged, oldValue, _value));
            TimeStamp = timeStamp;
        }

        public bool OnValueChanging(TagChangingEventArgs args)
        {
            ValueChanging?.Invoke(this, args);
            return args.Cancel;
        }

        public void OnValueChanged(TagChangedEventArgs args)
        {
            ValueChanged?.Invoke(this, args);
        }

        public virtual bool IsValidValue(dynamic value, out dynamic actualValue)
        {
            actualValue = value;
            return Value == null || !Value.Equals(actualValue) || !(value is IBasicObject);
        }

        public override string ToString()
        {
            //return _Dynamic == null ? string.Empty : JsonHelper.JsonSerialize(Dynamic);
            return Name;
        }
    }
}